package orbit

import (
	"sync"
	"sync/atomic"
	"time"

	"gddgame.cc/galaxy/satellite/formula/comet"
	"gddgame.cc/galaxy/utils/serialize"
)

type Client interface {
	SendCompress(comet.DE, []byte)
	Send(comet.DE, []byte)
	GetFrameRate() time.Duration
}

type Batch struct {
	max      int32
	Data     []interface{}
	Pool     sync.Pool
	Index    int32
	Mux      sync.RWMutex
	LastSend time.Time
}

func NewBatch(max int32) *Batch {
	batch := Batch{
		max:      max,
		Pool:     sync.Pool{},
		Index:    -1,
		Mux:      sync.RWMutex{},
		LastSend: time.Now(),
	}
	batch.Pool.New = func() interface{} {
		return make([]interface{}, 0, max)
	}
	batch.Data = make([]interface{}, 0, max) // batch.Pool.Get().([]interface{})
	return &batch
}

func (batch *Batch) Init() {
	batch.Index = -1
	batch.Data = batch.Data[0:0]
	batch.LastSend = time.Now()
}

func (batch *Batch) Write(c Client, event comet.DE, data []interface{}) {
	if data != nil {
		tmp := make([]interface{}, len(data))
		copy(tmp, data)
		batch.Mux.Lock()
		batch.Data = append(batch.Data, tmp)
		atomic.AddInt32(&batch.Index, 1)
		batch.Mux.Unlock()
	}
	batch.Flush(c, event, false)
}

func (batch *Batch) Flush(c Client, event comet.DE, force bool) {
	batch.Mux.RLock()
	if !force && batch.Index < batch.max-1 && time.Now().Sub(batch.LastSend) < c.GetFrameRate() {
		batch.Mux.RUnlock()
		return
	}
	batch.Mux.RUnlock()
	batch.Mux.Lock()
	if batch.Index == -1 {
		batch.Mux.Unlock()
		return
	}
	batch.LastSend = time.Now()
	Index := batch.Index
	Data := batch.Data
	batch.Index = -1
	batch.Data = make([]interface{}, 0, batch.max) // batch.Pool.Get().([]interface{})
	batch.Mux.Unlock()
	bag := serialize.GetBag()
	bag.SetList(Data[0 : Index+1])
	b := bag.Bytes()
	if len(b) > 100 {
		//fmt.Println(len(b), Index)
		c.SendCompress(event, b)
	} else {
		c.Send(event, b)
	}
	//batch.Pool.Put(Data[0:0])
}

func (batch *Batch) Clear() {
	batch.Init()
}
